﻿#include "Stdafx.h"
#include "Session.h"

using namespace SNMP;

Session::Session(void)
{
	this->m_ulngEntityRetry = DEFAULT_RETRY;
	this->m_ulngEntityTimeout = DEFAULT_TIMEOUT;
}

Session::~Session(void)
{
	SnmpClose(this->m_hSession);
}

void Session::CreateSessionHandle(System::IntPtr handle, SnmpApiCallbakcDelegate^ fCallback)
{
	this->m_hWnd =  (HWND)handle.ToPointer();
	System::Runtime::InteropServices::GCHandle gch = System::Runtime::InteropServices::GCHandle::Alloc(fCallback);
	System::IntPtr ip = System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(fCallback);
	SNMPAPI_CALLBACK cb = static_cast<SNMPAPI_CALLBACK>(ip.ToPointer());
	this->CreateSessionHandle(m_hWnd, cb);
	//gch.Free();
}

void Session::CreateSessionHandle(HWND hWnd, SNMPAPI_CALLBACK fCallback)
{
	UINT wMsg = NULL;
	LPVOID lpClientData = NULL;
	this->m_hSession = SnmpCreateSession(hWnd, wMsg, fCallback, lpClientData);
	SNMPAPI_STATUS status = SNMPAPI_SUCCESS;
	if(this->m_hSession == SNMPAPI_FAILURE)
	{
		status = SnmpGetLastError(this->m_hSession);
		SNMPManager::ErrorManage(status);
	}
}

void Session::CreateSessionHandle(System::IntPtr handle, Session^ session)
{
	session->m_hWnd = (HWND)handle.ToPointer();
	UINT wMsg = NULL;
	LPVOID lpClientData = NULL;
	UnmanagedSnmpApiCallbakcDelegate^ fCallback =  gcnew UnmanagedSnmpApiCallbakcDelegate(session, &Session::SnmpApiCallbakc);
	System::Runtime::InteropServices::GCHandle gch = System::Runtime::InteropServices::GCHandle::Alloc(fCallback);
	System::IntPtr ip = System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(fCallback);
	SNMPAPI_CALLBACK cb = static_cast<SNMPAPI_CALLBACK>(ip.ToPointer());
	session->m_hSession = SnmpCreateSession(session->m_hWnd, wMsg,  cb, lpClientData);
	//gch.Free();
	if(session->m_hSession == SNMPAPI_FAILURE)
	{
		SNMPAPI_STATUS status = SnmpGetLastError(session->m_hSession);
		SNMPManager::ErrorManage(status);
	}
}

PduData^ Session::CreatePdu(PduData::PduType pduType, signed long requestID, signed long errorStatus, signed long errorIndex, VariableBindingsList^ varbindlist)
{
	smiINT type = PduData::GetPduType(pduType);
	PduData^ pduData = gcnew PduData();
	pduData->m_vblVbl = varbindlist;
	pduData->CreatePduHandle(this->m_hSession, type, requestID, errorStatus, errorIndex, varbindlist->m_hVbl);
	return pduData;
}

VariableBindingsList^ Session::CreateVariableBindingsList()
{
	VariableBindingsList^ vbl = gcnew VariableBindingsList();
	vbl->CreateVblHandle(this->m_hSession);
	return vbl;
}

Entity^ Session::CreateEntity(System::Net::IPAddress^ IPAddress, unsigned long timeout, unsigned long retry)
{
	return this->CreateEntity(IPAddress->ToString(), timeout, retry);
}

Entity^ Session::CreateEntity(String^ IPAddress, unsigned long timeout, unsigned long retry)
{
	Entity^ entity = gcnew Entity();
	entity->CreateEntityHandle(this->m_hSession, IPAddress, timeout, retry);
	return entity;
}

Community^ Session::CreateCommunity(System::String^ context)
{
	Community^ community = gcnew Community();
	community->CreateContextHandle(this->m_hSession, context);
	return community;
}

void Session::Close(void)
{
	SNMPAPI_STATUS status = SnmpClose(this->m_hSession);
	if( status == SNMPAPI_FAILURE)
	{
		status =  SnmpGetLastError(NULL);
		SNMPManager::ErrorManage(status);
	}
	else
	{
		//return SnmpApiStatus::Success;
	}
}

void Session::SendMessage(System::String^ dstIpAddress, System::String^ community, PduData^ pdu)
{
	Entity^ srcEntity = this->CreateEntity(LOCALHOST_ADDRESS, this->EntityTimeout, this->m_ulngEntityRetry);
	Entity^ dstEntity = this->CreateEntity(dstIpAddress, this->EntityTimeout, this->m_ulngEntityRetry);
	Community^ comm = this->CreateCommunity(community);
	this->SendMessage(srcEntity, dstEntity, comm, pdu);
}

void Session::SendMessage(Entity^ srcEntity, Entity^ dstEntity, Community^ context, PduData^ pdu)
{
	SNMPAPI_STATUS status = SnmpSendMsg(this->m_hSession, srcEntity->m_hEntity, dstEntity->m_hEntity, context->m_hContext, pdu->m_hPdu);
	if(status == SNMPAPI_FAILURE)
	{
		status = SnmpGetLastError(this->m_hSession);
		SNMPManager::ErrorManage(status);
	}
	
	// free
	//srcEntity->Clear();
	//dstEntity->Clear();
	//context->Clear();
	//pdu->Clear();
	//status = SnmpFreeContext(srcEntity->m_hEntity);
	//status = SnmpFreeEntity(dstEntity->m_hEntity);
	//status = SnmpFreeEntity(context->m_hContext);
	//status = SnmpFreePdu(pdu->m_hPdu);
	//GC::Collect();
	
}

void Session::SendMessage(array<byte>^ originalData)
{
	HSNMP_ENTITY srcEntity = NULL;
	HSNMP_ENTITY dstEntity = NULL;
	HSNMP_CONTEXT context = NULL;
	HSNMP_PDU pdu = NULL;
	smiOCTETS msgBufDesc;
	msgBufDesc.len = originalData->Length;
	msgBufDesc.ptr = new smiBYTE[originalData->Length];
	for(int i = 0; i < originalData->Length;  i++)
	{
		msgBufDesc.ptr[i] = originalData[i];
	}

	SNMPAPI_STATUS status = SnmpDecodeMsg(this->m_hSession, &srcEntity, &dstEntity, &context, &pdu, &msgBufDesc);
	delete msgBufDesc.ptr;
	if(status == SNMPAPI_FAILURE)
	{
		status = SnmpGetLastError(this->m_hSession);
		SNMPManager::ErrorManage(status);
	}

	status = SnmpSendMsg(this->m_hSession, srcEntity, dstEntity, context, pdu);
	SnmpFreeContext(context);
	SnmpFreeEntity(dstEntity);
	SnmpFreeEntity(srcEntity);
	SnmpFreePdu(pdu);
	if(status == SNMPAPI_FAILURE)
	{
		status = SnmpGetLastError(this->m_hSession);
		SNMPManager::ErrorManage(status);
	}
}

void Session::ReceiveMessage(void)
{
	HSNMP_ENTITY outSrcEntity = NULL;
	HSNMP_ENTITY outDstEntity = NULL;
	HSNMP_CONTEXT outContext = NULL;
	HSNMP_PDU outPDU = NULL;
	SNMPAPI_STATUS status = SnmpRecvMsg(this->m_hSession, &outSrcEntity, &outDstEntity, &outContext, &outPDU);
	if(status ==  SNMPAPI_FAILURE)
	{
		status = SnmpGetLastError(this->m_hSession);
		SNMPManager::ErrorManage(status);
	}

	Entity^ srcEntity = gcnew Entity(this->m_hSession, outSrcEntity);
	Entity^ dstEntity = gcnew Entity(this->m_hSession, outDstEntity);
	Community^ Community = gcnew SNMP::Community(this->m_hSession, outContext);
	PduData^ pdu = gcnew PduData(this->m_hSession, outPDU);
	pdu->GetPduData();
	
	smiOCTETS msgBufDesc;
	array<byte>^ originalData;
	status = SnmpEncodeMsg(this->m_hSession, outSrcEntity, outDstEntity, outContext, outPDU, &msgBufDesc);
	if(status ==  SNMPAPI_FAILURE)
	{
		status = SnmpGetLastError(this->m_hSession);
		//SNMPManager::ErrorManage(status);
	}
	else
	{
		originalData = gcnew array<byte>(msgBufDesc.len);
		for(smiUINT32 i = 0; i < msgBufDesc.len; i++)
		{
			originalData[i] = msgBufDesc.ptr[i];
		}
	}


	ReceiveDataEventArgs^ e = gcnew ReceiveDataEventArgs(srcEntity, dstEntity, Community, pdu, originalData);
	if(pdu->m_intRequestId == Session::TRAVERSE_MIB_REQUEST_ID)
	{
		// Traverse MIB
		if(pdu->ErrorStatus == SNMP_ERROR_NOSUCHNAME)
		{
			this->m_bRecursioning = false;
		}
		else
		{
			String^ tempKey;
			Value^ tempVal;
			for each(KeyValuePair<String^, Value^> kvp in pdu->m_vblVbl->m_dicValueCollections)
			{
				tempKey = kvp.Key;
				tempVal = kvp.Value;
			}
			if(!this->m_dicValueCollections->ContainsKey(tempKey))
			{
				this->m_dicValueCollections->Add(tempKey,tempVal);
			}
			this->TraverseMIB2(srcEntity->IPAddress, Community->Context, tempKey);
		}
	}
	else
	{
		// Receive Message
		ReceiveData(this, e);

		if(pdu->Type == PduData::PduType::Trap)
		{
			ReceiveTrap(this, e);
		}
	}
	
	// free 
	srcEntity->Clear();
	dstEntity->Clear();
	Community->Clear();
	pdu->Clear();
	smiINT PDU_type;
	smiINT request_id;
	smiINT error_status;
	smiINT error_index;
	HSNMP_VBL vbl;
	status = SnmpGetPduData(outPDU, &PDU_type, &request_id, &error_status, &error_index, &vbl);
	status = SnmpFreeEntity(outSrcEntity);
	status = SnmpFreeEntity(outDstEntity);
	status = SnmpFreePdu(outPDU);
	status = SnmpFreeVbl(vbl);
	status = SnmpFreeContext(outContext);
	status = SnmpFreeDescriptor(SNMP_SYNTAX_OCTETS, (smiLPOPAQUE)&msgBufDesc);
	//GC::Collect();
	
}

SNMPAPI_STATUS Session::SnmpApiCallbakc(HSNMP_SESSION /*hSession*/, HWND /*hwnd*/, UINT /*wMsg*/, WPARAM wParam, LPARAM lParam, LPVOID /*lpClientData*/)
{
	if( wParam == 0){
		this->ReceiveMessage();
	}

	if(wParam == SNMPAPI_TL_TIMEOUT) 
	{
		if(lParam == Session::TRAVERSE_MIB_REQUEST_ID)
		{
			this->m_bRecursioning = false;
		}
		Timeout(this, gcnew EventArgs());
	}
	return SNMPAPI_SUCCESS;
}

void Session::Register(void)
{
	HSNMP_ENTITY srcEntity = NULL;
	HSNMP_ENTITY dstEntity = NULL;

	HSNMP_CONTEXT context = NULL;
	smiLPCOID notification = NULL;
	smiUINT32 state = SNMPAPI_ON;
	SNMPAPI_STATUS status = SnmpRegister(this->m_hSession,srcEntity,dstEntity,context,notification,state);
	if(status == SNMPAPI_FAILURE)
	{
		status = SnmpGetLastError(this->m_hSession);
		SNMPManager::ErrorManage(status);
	}
}

void Session::Register(Entity^ srcEntity, Entity^ dstEntity, Community^ context)
{
	smiLPCOID notification = NULL;
	smiUINT32 state = SNMPAPI_ON;
	SNMPAPI_STATUS status = SnmpRegister(this->m_hSession, srcEntity->m_hEntity, dstEntity->m_hEntity, context->m_hContext, notification, state);
	if(status == SNMPAPI_FAILURE)
	{
		status = SnmpGetLastError(this->m_hSession);
		SNMPManager::ErrorManage(status);
	}
}

void Session::TraverseMIB2(System::String^ ipAddress, System::String^ community, System::String^ oid)
{
	VariableBindingsList^ vbl = this->CreateVariableBindingsList();
	vbl->Add(oid, gcnew Value(Value::Syntax::Octets, String::Empty));
	PduData^ pdu = this->CreatePdu(PduData::PduType::GetNext, TRAVERSE_MIB_REQUEST_ID, 0, 0, vbl);
	SendMessage(ipAddress, community, pdu);
}

Dictionary<String^, Value^>^ Session::TraverseMIB(System::String^ ipAddress, System::String^ community, System::String^ oid)
{
	this->m_bRecursioning = true;
	if(this->m_dicValueCollections == nullptr)
	{
		this->m_dicValueCollections = gcnew Dictionary<String^, Value^>();
	}
	else
	{
		this->m_dicValueCollections->Clear();
	}
	this->TraverseMIB2(ipAddress, community, oid);
	while(this->m_bRecursioning);
	return this->m_dicValueCollections;
}